﻿using System;
using System.Collections.Generic;
using System.Linq;
using Diploma.Models.IRepositories;
using Diploma.Models.Utils.PasswordsUtils;

namespace Diploma.Models.Repositories
{
    internal class StudentsRepository : IStudentsRepository
    {
        private readonly DiplomaEntities dataContext;

        public StudentsRepository(DiplomaEntities dataContext)
        {
            this.dataContext = dataContext;
        }

        public IEnumerable<Student> GetStudents()
        {
            return dataContext.Users.OfType<Student>().Where(u => u.DeletionDate == null);
        }

        public Student GetStudent(Guid id)
        {
            return dataContext.Users.OfType<Student>().FirstOrDefault(u => u.UserId == id && u.DeletionDate == null);
        }

        public Student GetStudentByCollaborationRoom(Guid id)
        {
            CollaborationPropose colprop = dataContext.CollaborationProposes.FirstOrDefault(c => c.CollaborationRoomId == id && c.IsApproved == true && c.DeletionDate == null);

            return colprop.Student;
        }

        public IEnumerable<Student> GetMagisterStudentsWithoutPractice()
        {
            var studentList = new List<Student>();
            var magister = dataContext.QualificationLevels.FirstOrDefault(r => r.QualificationLevelId == 2);
            if (magister == null)
                throw new ArgumentNullException("magister");
            foreach (Student student in dataContext.Users.OfType<Student>())
            {
                if (student.CollaborationRoom != null && student.StudentsGroup != null && student.DeletionDate == null)
                {
                    if (student.StudentsGroup.QualificationLevelId == magister.QualificationLevelId &&
                        student.CollaborationRoom.PracticeLecturer == null)
                    {
                        studentList.Add(student);
                    }
                }
            }
            return studentList;
        }

        public IEnumerable<Student> GetStudentsForLecturerPractice(Guid lecturerId)
        {
            var studentList = new List<Student>();
            foreach (Student student in dataContext.Users.OfType<Student>())
            {
                if(student.CollaborationRoom != null && student.CollaborationRoom.PracticeLecturer == lecturerId)
                studentList.Add(student);
            }
            return studentList;
        }

        public void AttachLecturerPracticeForStudent(Guid userId, Guid lecturerId)
        {
            var student = GetStudent(userId);
            if(student != null)
            {
                student.CollaborationRoom.PracticeLecturer = lecturerId;
                dataContext.SaveChanges();
            }
        }

        public void DetachLecturerPracticeFromStudent(Guid userId)
        {
            var student = GetStudent(userId);
            if (student != null)
            {
                student.CollaborationRoom.PracticeLecturer = null;
                dataContext.SaveChanges();
            }
        }

        public void UpdateUserProperties(Guid userId, bool themeWithOrder, bool notice, bool feedback, bool signatureNormControl, bool preliminaryPresentation,
            bool signatureLecturer, bool signatureStudent, bool signatureConsultants, bool slides, bool archive)
        {
            var user = GetStudent(userId);
            user.CollaborationRoom.ThemeWithOrder = themeWithOrder;
            user.CollaborationRoom.Notice = notice;
            user.CollaborationRoom.Feedback = feedback;
            user.CollaborationRoom.SignatureNormControl = signatureNormControl;
            user.CollaborationRoom.PreliminaryPresentation = preliminaryPresentation;
            user.CollaborationRoom.SignatureLecturer = signatureLecturer;
            user.CollaborationRoom.SignatureStudent = signatureStudent;
            user.CollaborationRoom.SignatureConsultants = signatureConsultants;
            user.CollaborationRoom.Slides = slides;
            user.CollaborationRoom.Archive = archive;
            dataContext.SaveChanges();
        }

        public bool IsAllPointSuccess(Guid userId)
        {
            Student student = DataManager.Instance.StudentsRepository.GetStudent(userId);
            IEnumerable<Task> tasks = student.CollaborationRoom == null
                                          ? null
                                          : student.CollaborationRoom.Tasks.Where(t => t.DeletionDate == null);
            tasks = from t in tasks
                    where t.IsCompleted == false &&
                          t.DeletionDate == null
                    select t;
            if (Enumerable.Any(dataContext.CheckPoints.OfType<GlobalTask>().Where(gt => gt.DeletionDate == null && gt.QualificationLevelId == student.StudentsGroup.QualificationLevelId).DefaultIfEmpty(), gt => !DataManager.Instance.GlobalTasksCompletionsRepository.GetCompletion(gt, student).Value))
            {
                return false;
            }
            return !tasks.Any();
        }

        public void UpdateUserWorkPlace(Guid id, bool placement)
        {
            var user = GetStudent(id);
            if (user.CollaborationRoom != null)
            {
                user.CollaborationRoom.Placement = placement;
                dataContext.SaveChanges();
            }
        }

        public void UpdatePracticePlace(Guid id, Guid? practicePlaceId)
        {
            var student = DataManager.Instance.StudentsRepository.GetStudent(id);
            if(student.CollaborationRoom != null)
            {
                student.CollaborationRoom.PracticePlace = practicePlaceId;
            }
            dataContext.SaveChanges();
        }

        public IEnumerable<Student> GetStudentsForCompany(Guid companyId)
        {
            return GetStudents().Where(student => student.CollaborationRoom != null && student.CollaborationRoom.PracticePlace == companyId).ToList();
        }

        public IEnumerable<Student> GetStudentsForCompany(int qualificationLevelId, Guid companyId)
        {
            return GetStudents().Where(student => student.CollaborationRoom != null && student.CollaborationRoom.PracticePlace == companyId && student.StudentsGroup.QualificationLevelId == qualificationLevelId).ToList();
        }

        public string GetStudentPracticeLecturer(Guid studentId)
        {
            var student = GetStudent(studentId);
            if (student.StudentsGroup.QualificationLevelId.HasValue && student.StudentsGroup.QualificationLevelId.Value == 2)
            {
                // For magisters
                var lecturerId = student.CollaborationRoom.PracticeLecturer;
                if (lecturerId.HasValue)
                {
                    return DataManager.Instance.LecturersRepository.GetLecturer(lecturerId.Value).FullName;
                }
            }
            else
            {
                // For other students
                if (student.CollaborationRoom.PracticePlace.HasValue && student.StudentsGroup.QualificationLevelId.HasValue)
                {
                    var company = DataManager.Instance.CompaniesRepository.Get(student.CollaborationRoom.PracticePlace.Value);
                    var lecturerId = dataContext.LecturerInCompanies.Where(r => r.CompanyId == company.Id).First(r => r.LevelId == student.StudentsGroup.QualificationLevelId.Value).LecturerId;
                    return DataManager.Instance.LecturersRepository.GetLecturer(lecturerId).FullName;
                }
            }

            return String.Empty;
        }

        public void Update(Student newStudent)
        {
            Student oldStudent = GetStudent(newStudent.UserId);
            if (oldStudent == null)
                throw new Exception();

            oldStudent.InformationAbout = newStudent.InformationAbout;
            oldStudent.Name = newStudent.Name;
            oldStudent.Surname = newStudent.Surname;
            oldStudent.Patronymic = newStudent.Patronymic;
            oldStudent.StudentsGroupId = newStudent.StudentsGroupId;
            oldStudent.Email = newStudent.Email;

            dataContext.SaveChanges();
        }

        public void Add(Student student)
        {
            student.UserId = Guid.NewGuid();
            student.Password = PasswordGenerator.Encrypt(PasswordGenerator.GeneratePassword());
            dataContext.AddToUsers(student);
            dataContext.SaveChanges();
        }

        public void Delete(Student student)
        {
            student.MarkForDeletion();
            dataContext.SaveChanges();
        }

        public CollaborationPropose GetCollaborationPropose(Student student)
        {
            if (student.CollaborationPropos == null)
                return null;
            if (student.CollaborationPropos.DeletionDate != null)
                return null;

            return student.CollaborationPropos;
        }

        public IEnumerable<Student> GetDeletedStudents()
        {
            return
                dataContext.Users.OfType<Student>().Where(s => s.DeletionDate != null).OrderBy(g => g.Surname).ThenBy(
                    g => g.Name).ThenBy(g => g.Patronymic);
        }

        public Dictionary<QualificationLevel, IEnumerable<KeyValuePair<StudentsGroup, IEnumerable<Student>>>>
            GetDeletedStudentsGroupedByGroupAndLevel()
        {
            IEnumerable<Student> deletedStuds = GetDeletedStudents();

            return (from KeyValuePair<StudentsGroup, IEnumerable<Student>> s in
                        (from stud in deletedStuds
                         group stud by stud.StudentsGroup
                         into sg
                         select sg).ToDictionary(f => f.Key, f => (IEnumerable<Student>) f.ToList())
                    group s by s.Key.QualificationLevel
                    into sd
                    select sd).ToDictionary(f => f.Key,
                                            f =>
                                            (IEnumerable<KeyValuePair<StudentsGroup, IEnumerable<Student>>>) f.ToList());
        }

        public IEnumerable<Student> GetStudents(Guid groupId)
        {
            return dataContext.Users.OfType<Student>()
                .Where(r => r.StudentsGroupId == groupId)
                .Where(s => s.DeletionDate == null)
                .OrderBy(g => g.Surname)
                .ThenBy(g => g.Name)
                .ThenBy(g => g.Patronymic);
        }

        public Dictionary<QualificationLevel, IEnumerable<KeyValuePair<StudentsGroup, IEnumerable<Student>>>>
            GetStudentsGrouped(StudentsGroup group)
        {
            var qldict =
                new Dictionary<QualificationLevel, IEnumerable<KeyValuePair<StudentsGroup, IEnumerable<Student>>>>();
            var grdict = new List<KeyValuePair<StudentsGroup, IEnumerable<Student>>>();

            grdict.Add(new KeyValuePair<StudentsGroup, IEnumerable<Student>>(group,
                                                                             group.Students.Where(
                                                                                 s => s.DeletionDate == null).OrderBy(
                                                                                     g => g.Surname).ThenBy(g => g.Name)
                                                                                 .ThenBy(g => g.Patronymic)));

            qldict.Add(group.QualificationLevel, grdict);
            return qldict;
        }

        public void Restore(Guid studentId)
        {
            Student deletedStudent = GetDeletedStudents().FirstOrDefault(s => s.UserId == studentId);
            if (deletedStudent == null)
                throw new Exception("Student not found");
            deletedStudent.UnmarkFromDeletion();
            dataContext.SaveChanges();
        }
    }
}